package lime.middleware;

/**
 * <p>Title: Lime</p>
 * <p>Description: Lime</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: DII - DISMI</p>
 * @author Marco Mamei
 * @version 1.0
 */

import java.util.*;

import emulator.utils.*;
import emulator.peers.*;

import tuplespace.tuples.*;
import tuplespace.middleware.*;
import eventengine.middleware.*;
import corepeerservices.middleware.*;

import lime.tuples.*;

public class Lime implements LimeInterface {
 /* This is the main socket used for lime communication */
 static final String LIME_SOCKET = "core";
 //static final String LIME_ADDRESS = "224.0.0.1";
 //static final int LIME_PORT = 8000;

 /* This is the list of tuples that lime does not take into consideration.
 Having such a list is very important for optimization. For example, tuples
 coming from frequent sensor readings e.g. internal clock should not be broadcasted */
 String[] tuplesBlackList = {"tuplespace.tuples.SensorTuple"};

 /* must be friendly to be accessed by the limeEngine */
 PeerInterface_ApplicationSide peer;
 DiffusionBuffer diffusionBuffer;
 TupleSpace ts;
 VirtualTupleSpace vts;
 private LimeNetworkEngine limeNetworkEngine;
 private LimeTupleEngine limeTupleEngine;
 private CoreMiddleware core;

 public Lime(PeerInterface_ApplicationSide peer) {
  this.peer = peer;
  this.diffusionBuffer = new DiffusionBuffer(peer,LIME_SOCKET);
  ts = new TupleSpace(peer.toString());
  vts = new VirtualTupleSpace(peer.toString());
  core = new CoreMiddleware(peer);


  /* create lime socket */
  //peer.createSocketMulticast(LIME_SOCKET,LIME_ADDRESS,LIME_PORT);

  /* set up lime engine */
  limeNetworkEngine = new LimeNetworkEngine(this,core);
  limeTupleEngine = new LimeTupleEngine(this);
 }

 /* this method allows to install a event dispatcher in charge of notifying
 what is happening in the lime tuple spaces */
 public void setOutsideEvent(EventInterface tsEvent, EventInterface vtsEvent) {
  ts.setOutsideEvent(tsEvent);
  vts.setOutsideEvent(vtsEvent);
 }

 /*****************************************************************************/
 /*                     LINDA SPECIFIC INTERFACE                              */
 /*****************************************************************************/

 /* this method inserts a 'tuple' into the tuplespace */
 public void write (Tuple tuple) {
  ts.write(tuple);
  limeTupleEngine.wrote(tuple);
 }

 /* this non-blocking method reads all the tuple mathcing the 'template' from the tuplespace */
 public Vector read (Tuple template) {
  return ts.read(template);
 }

 /* this non-blocking method extracts all the tuple matching the 'template' form the tuplespace */
 public Vector extract (Tuple template) {
  Vector v = ts.extract(template);
  limeTupleEngine.extracted(v);
  return v;
 }

 /* this method reads a tuple on the basis of its id in the local tuple space */
 public Tuple keyrd (Tuple template) {
  return ts.keyrd(template);
 }
 /* this method extracts a tuple on the basis of its id in the local tuple space */
 public Tuple keyextr (Tuple template) {
  Tuple t = ts.keyextr(template);
  if(t != null)
  {
   Vector v = new Vector();
   v.add(t);
   limeTupleEngine.extracted(v);
  }
  return t;
 }

 /*****************************************************************************/
 /*                      LIME SPECIFIC INTERFACE                              */
 /*****************************************************************************/

 /* this method reads the tuples in the peer's one hop neighborhood on the key
 basis */
 public Vector keyrdOneHop (Tuple template) {
  return vts.keyrd(template);
 }
 /* this method reads the tuples in the peer's one hop neighborhood */
 public Vector rdOneHop(Tuple template) {
  return vts.read(template);
 }

 /*****************************************************************************/
 /*               PRIVATE LOW LEVEL COMMODITY METHODS                         */
 /*****************************************************************************/
 /* this method receives the String representing a serialized tuple. And it sate
 whether the tuple is or not in the black list */
 boolean isInBlackList(String flatTuple) {
  String[] parsed = StaticUtilities.splitMessage(flatTuple,'<','>');
  String className = parsed[0];
  for(int i=0;i<tuplesBlackList.length;i++)
  {
   if(className.equalsIgnoreCase(tuplesBlackList[i]))
    return true;
  }
  return false;
 }


 /*****************************************************************************/
 /*                             ACTIVE METHOD                                 */
 /*****************************************************************************/
 public void step(int time) {
  /* here the diffusion buffer is flushed on the netowrk, by broadcasting the
  relative tuples */
  diffusionBuffer.fire();

  limeNetworkEngine.step(time);
 }
}



/* this class is used to implement the concept of information diffusion rather
than information floding:

In the flooding mechanism, each individual node periodically broadcasts (pushes)
information about itself. Whenever a node receives a broadcast message, it stores
it and immediately forwards it by rebroadcasting the message. Obviously, this method
is not scalable, due to messages flooding over the network, especially in high density
networks.

In the diffusion mechanism  each node broadcasts information about itself and the
other nodes it knows about. Whenever a node receives broadcast information, it updates
its stored information and defers forwarding the information to the next broadcast
period, at which time it broadcasts its updated information. The diffusion mechanism
is scalable, since the number of broadcast messages is limited and they do not flood
the network. */

class DiffusionBuffer {
 private PeerInterface_ApplicationSide peer;
 String SOCKET;
 private HashMap buff;
 private Object[] msgs;

 DiffusionBuffer(PeerInterface_ApplicationSide peer, String SOCKET) {
  this.peer = peer;
  this.SOCKET = SOCKET;
  buff = new HashMap();
 }

 public void store(String id, String msg) {
  buff.put(id,msg);
 }

 public void fire() {
  if(buff.size() > 0)
  {
   System.out.println("\n\n"+peer);
   msgs = buff.values().toArray();
   
   for(int i=0; i<msgs.length; i++) {
	   	String mess = (String)msgs[i];
		System.out.println(mess);
	    peer.send(SOCKET,mess);
   }  
  }
  buff.clear();
 }
}